/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MSDP_SPATIAL_COORDINATE_SYSTEM_H
#define MSDP_SPATIAL_COORDINATE_SYSTEM_H

#include <functional>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "msdp_algorithm_interface.h"
#include "spatial_common.h"

namespace OHOS {
namespace Msdp {
class SpatialCoordinateSystem :
    public std::enable_shared_from_this<SpatialCoordinateSystem>,
    public MsdpAlgorithmInterface::SpatialCallback {
public:
    class UpdateListener {
    public:
        virtual void OnDeviceAdded(const DeviceInfo& device,
            const DeviceCoordinate& coordinate) = 0;
        virtual void OnDeviceRemoved(const DeviceInfo& device) = 0;
        virtual void OnDeviceCoordinateUpdate(
            const DeviceInfo& device, const DeviceCoordinate& coordinate) = 0;
        virtual void OnUpdateEnd() = 0;
    };
    SpatialCoordinateSystem();
    ~SpatialCoordinateSystem();
    void Init();
    void UpdateCenterNetworkId(const std::string& networkId);
    void RegisterUpdateListener(const std::shared_ptr<UpdateListener> &listener);
    void UnRegsterUpdateLister(const std::shared_ptr<UpdateListener> &listener);

    bool IsEmpty();
    bool IsExistedDevice(const DeviceInfo& device);
    bool UpdateDevices(const std::vector<DeviceInfo>& devices, bool newDeviceOnly = true);
    bool MergeMap(const std::shared_ptr<SpatialMap>& updateMap);
    bool SyncMap(const std::shared_ptr<SpatialMap>& updateMap);
    std::shared_ptr<SpatialMap> Commit();
    uint32_t GetTopCommit();
    bool IsInHistory(uint32_t commit);

    std::shared_ptr<DeviceCoordinate> GetCoordinate(const DeviceInfo& device, bool& success);
    float GetOrientation();

    SpatialMap GetSpatialMap();
    void InsertLocalDeviceInfo(const DeviceInfo& centerDevice);
private:
    static const time_t KEEP_TIME_MAX = 600000;
    using EventAction = std::function<bool(const SpatialEvent& Event)>;
    void OnDeviceAdd(DeviceInfo deviceInfo,
        DeviceCoordinate& coordinate, const DeviceCoordinate& offset);
    void OnCoordinateUpdate(DeviceInfo deviceInfo,
        DeviceCoordinate& coordinate, const DeviceCoordinate& offset, DeviceCoordinate& coordinateMap);
    void GetMockDeviceInfo();
    void CheckSpatialEvent(const SpatialEvent& event);
    void OnResult(const SpatialEvent& event) override;
    void OnEnd() override;
    void DumpSpatialEvent(const SpatialEvent& event);
    void DumpSpatialMap(const SpatialMap& spatialMap);
    void MergeHistory(const std::shared_ptr<SpatialMap>& map);
    bool TransformCoordinate(DeviceCoordinate& coordinate,
        const DeviceCoordinate& offset);
    void UpdateByEvent(const SpatialEvent& event);
    void UpdateByMap(const std::shared_ptr<SpatialMap>& map, const DeviceCoordinate& offset);
    bool IsCoordinateOld(const DeviceInfo& device, const DeviceCoordinate coordinate)
    {
        return (coordinate.keepingTime > KEEP_TIME_MAX);
    }

    SpatialMap map_;
    std::list<EventAction> eventActions_;
    std::list<std::shared_ptr<UpdateListener>> listeners_;
    std::mutex mutex_;
    DeviceInfo queryInfo_;
    SpatialEvent event_;
    bool calculating_ {false};
};
} // namespace Msdp
} // namespace OHOS

#endif // MSDP_SPATIAL_COORDINATE_SYSTEM_H
